/*
 * Copyright 2012 The Netty Project
 *
 * The Netty Project licenses this file to you under the Apache License,
 * version 2.0 (the "License"); you may not use this file except in compliance
 * with the License. You may obtain a copy of the License at:
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations
 * under the License.
 */
package net.jueb.sniff4j.socksCroe;
import net.jueb.sniff4j.extr.base.AbstractRuntime;
import net.jueb.sniff4j.extr.base.ChannelContext;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelPromise;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.socks.SocksAuthRequest;
import io.netty.handler.codec.socks.SocksAuthRequestDecoder;
import io.netty.handler.codec.socks.SocksAuthResponse;
import io.netty.handler.codec.socks.SocksAuthScheme;
import io.netty.handler.codec.socks.SocksAuthStatus;
import io.netty.handler.codec.socks.SocksCmdRequest;
import io.netty.handler.codec.socks.SocksCmdRequestDecoder;
import io.netty.handler.codec.socks.SocksCmdType;
import io.netty.handler.codec.socks.SocksInitResponse;
import io.netty.handler.codec.socks.SocksRequest;
import io.netty.util.internal.logging.InternalLogger;
import io.netty.util.internal.logging.InternalLoggerFactory;

/**
 * 业务处理handler
 * @author helin
 *
 */
@ChannelHandler.Sharable
public final class SocksServerHandler extends SimpleChannelInboundHandler<SocksRequest> {
	protected static final InternalLogger log = InternalLoggerFactory.getInstance(SocksServerHandler.class);
    private static final String name = "SOCKS_SERVER_HANDLER";

    public static String getName() {
        return name;
    }
    public SocksServerHandler() {
    	
	}
    
    /**
     * 是否开启认证
     * @return
     */
    public boolean isEnableAuth(ChannelHandlerContext ctx)
    {
    	//TODO
    	AbstractRuntime rt=AbstractRuntime.getRuntime(ctx.channel());
    	return rt.getBootConfig().isAuth;
    }
    
    public boolean userLogin(ChannelHandlerContext ctx,SocksAuthRequest socksUser)
    {
    	//TODO 连接后,用户认证
    	AbstractRuntime rt=AbstractRuntime.getRuntime(ctx.channel());
    	if(rt.getAllowUsers().contains(socksUser.username()))
    	{
    		//初始化一个管道上下文
    		rt.initChannelContext(ctx.channel(),new ChannelContext(socksUser.username(),socksUser.password()));
    		log.debug("用户:"+socksUser.username()+"连接,此次连接上下文初始化完成!");
    		return true;
    	}
    	log.debug("用户:"+socksUser.username()+"被拒绝连接!");
    	return false;
    }
    
    @Override
    public void channelRead0(ChannelHandlerContext ctx, SocksRequest socksRequest) throws Exception 
    {
    	switch (socksRequest.requestType()) {
            case INIT: {
//            	auth support example
        		//判断认证
        		if(isEnableAuth(ctx))
        		{//TODO
        			//设置下次要执行的处理器-获取认证
                    ctx.pipeline().addFirst("socksAuthRequestDecoder",new SocksAuthRequestDecoder());//协商认证协议
                    ctx.write(new SocksInitResponse(SocksAuthScheme.AUTH_PASSWORD));//回复认证协议类型
        		}else
        		{//无认证
        		    //读取协商请求
                	ctx.pipeline().addFirst(SocksCmdRequestDecoder.getName(), new SocksCmdRequestDecoder());
                    //回复协商协议 如果是无认证，则客户端下一步就直接发送转发请求了
                	ctx.write(new SocksInitResponse(SocksAuthScheme.NO_AUTH));
        		}
                break;
            }
            case AUTH:
            	//读取认证
            	SocksAuthRequest socksUser=(SocksAuthRequest)socksRequest;
            	if(socksUser!=null && userLogin(ctx, socksUser))
            	{//如果有，且认证成功
            		ctx.pipeline().addFirst(SocksCmdRequestDecoder.getName(), new SocksCmdRequestDecoder());
            		ctx.write(new SocksAuthResponse(SocksAuthStatus.SUCCESS));
            		break;//认证成功并跳出
            	}else
            	{
                	ctx.write(new SocksAuthResponse(SocksAuthStatus.FAILURE));
            		ctx.close();
                    break;//认证失败，则关闭连接并且跳出
            	}
            case CMD://转发请求命令
                SocksCmdRequest req = (SocksCmdRequest) socksRequest;
                if (req.cmdType() == SocksCmdType.CONNECT) 
                {//如果是连接类型的代理请求，则添加相应handler处理
                    ctx.pipeline().addLast(SocksServerConnectHandler.getName(), new SocksServerConnectHandler());
                    ctx.pipeline().remove(this);//并移除当前handler
                    ctx.fireChannelRead(socksRequest);//把当前的request设置为下一个handler的处理请求
                } else 
                {
                    ctx.close();
                }
                break;
            case UNKNOWN:
                ctx.close();
                break;
        }
    }

    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
        ctx.flush();
    }
   
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable throwable) throws Exception {
//        throwable.printStackTrace();
        SocksServerUtils.closeOnFlush(ctx.channel());
    }
}
